package com.linkhub.oms.dao.mpservice.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.extension.mapping.config.DeepConfig;
import com.github.yulichang.extension.mapping.relation.Relation;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.github.yulichang.wrapper.resultmap.MFunc;
import com.github.yulichang.wrapper.resultmap.MybatisLabelFree;
import com.google.common.collect.Lists;
import com.linkhub.oms.common.enums.mp.*;
import com.linkhub.oms.common.util.QtStreamUtil;
import com.linkhub.oms.dao.mapper.OrderEntityMapper;
import com.linkhub.oms.dao.mpservice.IOrderService;
import com.linkhub.oms.dao.mpservice.IShopService;
import com.linkhub.oms.entity.LocalProductEntity;
import com.linkhub.oms.entity.OrderEntity;
import com.linkhub.oms.entity.OrderProductEntity;
import com.linkhub.oms.entity.ShopProductSkuEntity;
import com.linkhub.oms.entity.ShopProductSkuMappingEntity;
import com.linkhub.oms.entity.resulttype.order.OrderInfoDO;
import com.linkhub.oms.entity.resulttype.order.OrderLocalProductDO;
import com.linkhub.oms.entity.resulttype.order.OrderShopProductDO;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Supplier;

/**
 * <p>
 * 订单表 服务实现类
 * </p>
 *
 * @author echo
 * @since 2024-03-28
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderEntityMapper, OrderEntity> implements IOrderService {

    @Resource
    private OrderEntityMapper orderEntityMapper;

    @Resource
    private IShopService shopService;

    @Override
    public List<OrderEntity> listSynchronizedShopOrders( String sysShopId,
                                                         Collection<String> orderNumbers,
                                                         SFunction<OrderEntity, ?>... columns ) {
        return super.lambdaQuery()
                .select(columns)
                .eq(OrderEntity::getSysShopId, sysShopId)
                .in(OrderEntity::getOrderNumber, orderNumbers)
                .list();
    }

    @Override
    public List<OrderEntity> listShopOrders( String sysShopId, CustomerBusinessStatusEnum customerBusinessStatus ) {
        return super.lambdaQuery()
                .select(OrderEntity::getSysOrderId, OrderEntity::getOrderStatus, OrderEntity::getId)
                .eq(OrderEntity::getSysShopId, sysShopId)
                .eq(OrderEntity::getCustomerBusinessStatus, customerBusinessStatus)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .list();

    }

    @Override
    public List<OrderEntity> listBySysOrderIds( List<String> sysOrderIds ) {
        if (CollUtil.isEmpty(sysOrderIds)) {
            return Lists.newArrayListWithCapacity(0);
        }

        return super.lambdaQuery()
                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .list();
    }

    @Override
    public List<OrderInfoDO> queryOrderWithLocalProducts( List<String> sysOrderIds ) {
        if (CollUtil.isEmpty(sysOrderIds)) {
            return Lists.newArrayListWithCapacity(0);
        }

        MPJLambdaWrapper<OrderEntity> lambdaWrapper = JoinWrappers.lambda(OrderEntity.class);
        MFunc<MybatisLabelFree.Builder<OrderLocalProductDO>> localProductResultMap = map ->
                map.result(OrderProductEntity::getSysOrderId, OrderLocalProductDO::getSysOrderId)
                        .result(OrderProductEntity::getPrice, OrderLocalProductDO::getPrice)
                        .result(OrderProductEntity::getCurrentQuantity, OrderLocalProductDO::getCurrentQuantity)
                        .result(OrderProductEntity::getSysShopId, OrderLocalProductDO::getSysShopId)
                        .result(OrderProductEntity::getShopProductId, OrderLocalProductDO::getShopProductId)
                        .result(OrderProductEntity::getShopSkuId, OrderLocalProductDO::getShopSkuId)
                        .result(OrderProductEntity::getCurrencyCode, OrderLocalProductDO::getCurrencyCode)

                        .result(LocalProductEntity::getSkuId, OrderLocalProductDO::getSkuId)
                        .result(LocalProductEntity::getSku, OrderLocalProductDO::getSku)
                        .result(LocalProductEntity::getSpu, OrderLocalProductDO::getSpu)
                        .result(LocalProductEntity::getImgUrl, OrderLocalProductDO::getImgUrl)
                        .result(LocalProductEntity::getProductEnTitle, OrderLocalProductDO::getProductEnTitle);

        lambdaWrapper.select(OrderEntity::getSysOrderId)
                .selectCollection(OrderInfoDO::getOrderLocalProducts, localProductResultMap)

                .leftJoin(OrderProductEntity.class, OrderProductEntity::getSysOrderId, OrderEntity::getSysOrderId)
                .leftJoin(LocalProductEntity.class, LocalProductEntity::getSkuId, OrderProductEntity::getSkuId)

                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .eq(OrderProductEntity::getProductType, OrderProductTypeEnum.LOCAL);

        return orderEntityMapper.selectJoinList(OrderInfoDO.class, lambdaWrapper);
    }

    @Override
    public List<OrderInfoDO> queryOrderWithShopProducts( List<String> sysOrderIds ) {

        if (CollUtil.isEmpty(sysOrderIds)) {
            return Lists.newArrayListWithCapacity(0);
        }

        MPJLambdaWrapper<OrderEntity> lambdaWrapper = JoinWrappers.lambda(OrderEntity.class);
        MFunc<MybatisLabelFree.Builder<OrderShopProductDO>> orderShopProductResultMap = map ->
                map.result(OrderProductEntity::getSysShopId, OrderShopProductDO::getSysShopId)
                        .result(OrderProductEntity::getShopProductId, OrderShopProductDO::getProductId)
                        .result(OrderProductEntity::getShopSkuId, OrderShopProductDO::getSkuId)
                        .result(OrderProductEntity::getSkuTitle, OrderShopProductDO::getSku)

                        .result(ShopProductSkuEntity::getImgUrl, OrderShopProductDO::getImgUrl)
                        .result(ShopProductSkuEntity::getTitle, OrderShopProductDO::getTitle)
                        .result(OrderProductEntity::getSysOrderId, OrderShopProductDO::getSysOrderId)
                        .result(OrderProductEntity::getPrice, OrderShopProductDO::getPrice)
                        .result(OrderProductEntity::getCurrentQuantity, OrderShopProductDO::getCurrentQuantity)
                        .result(OrderProductEntity::getCurrencyCode, OrderShopProductDO::getCurrencyCode)
                        .result(LocalProductEntity::getSkuId, OrderShopProductDO::getLocalSkuId)
                        .result(LocalProductEntity::getSku, OrderShopProductDO::getLocalSku)
                        .result(LocalProductEntity::getSpu, OrderShopProductDO::getLocalSpu)
                        .result(LocalProductEntity::getImgUrl, OrderShopProductDO::getLocalImgUrl)
                        .result(LocalProductEntity::getProductEnTitle, OrderShopProductDO::getLocalProductEnTitle);

        lambdaWrapper.select(OrderEntity::getSysOrderId)
                .selectCollection(OrderInfoDO::getOrderShopProducts, orderShopProductResultMap)

                .leftJoin(OrderProductEntity.class, OrderProductEntity::getSysOrderId, OrderEntity::getSysOrderId)

                .leftJoin(ShopProductSkuEntity.class, on ->
                        on.eq(OrderProductEntity::getSysShopId, ShopProductSkuEntity::getSysShopId)
                                .eq(OrderProductEntity::getShopSkuId, ShopProductSkuEntity::getSkuId))

                .leftJoin(ShopProductSkuMappingEntity.class, on ->
                        on.eq(ShopProductSkuMappingEntity::getSysShopId, ShopProductSkuEntity::getSysShopId)
                                .eq(ShopProductSkuMappingEntity::getShopSkuId, ShopProductSkuEntity::getSkuId))

                .leftJoin(LocalProductEntity.class, LocalProductEntity::getSkuId, ShopProductSkuMappingEntity::getSkuId)

                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .eq(OrderProductEntity::getProductType, OrderProductTypeEnum.THIRD_PARTY);


        return orderEntityMapper.selectJoinList(OrderInfoDO.class, lambdaWrapper);
    }

    @Override
    public List<OrderEntity> deepMappingOrders( List<OrderEntity> orders ) {

        if (CollUtil.isEmpty(orders)) {
            return orders;
        }
        return Relation.mpjGetRelation(orders, DeepConfig.defaultConfig());
    }

    @Override
    public void updateCustomerBizState( List<String> sysShopIds,
                                        List<String> sysOrderIds,
                                        CustomerBusinessStatusEnum beforeState,
                                        CustomerBusinessStatusEnum afterState ) {

        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setCustomerBusinessStatus(afterState);
        this.updateBizState(sysShopIds,
                sysOrderIds,
                OrderEntity::getCustomerBusinessStatus,
                beforeState,
                orderEntity);


    }

    @Override
    public void updateEmployeeBizState( List<String> sysOrderIds,
                                        EmployeeBusinessStatusEnum beforeState,
                                        EmployeeBusinessStatusEnum afterState ) {

        OrderEntity orderEntity = new OrderEntity();
        orderEntity.setEmployeeBusinessStatus(afterState);
        this.updateBizState(null,
                sysOrderIds,
                OrderEntity::getEmployeeBusinessStatus,
                beforeState, orderEntity);
    }

    @Override
    public void updateOrderInfo( String sysOrderId, OrderEntity update ) {
        lambdaUpdate()
                .eq(OrderEntity::getSysOrderId, sysOrderId)
                .update(update);

    }

    @Override
    public void updateOrderInfos( List<String> sysOrderIds, OrderEntity update ) {
        if (CollUtil.isEmpty(sysOrderIds)) {
            return;
        }

        lambdaUpdate()
                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .update(update);
    }

    public boolean updateOrderStatus(String sysOrderId,
                                  CustomerOrderStatusEnum before,
                                  CustomerOrderStatusEnum update,
                                  EmployeeOrderStatusEnum employeeBefore,
                                  EmployeeOrderStatusEnum employeeUpdate,
                                  OrderStockStatusEnum orderStockStatusEnumBefore,
                                  OrderStockStatusEnum orderStockStatusEnumUpdate) {
        return lambdaUpdate().eq(OrderEntity::getSysOrderId, sysOrderId)
                .eq(OrderEntity::getCustomerOrderStatus, before)
                .eq(OrderEntity::getEmployeeOrderStatus, employeeBefore)
                .eq(OrderEntity::getOrderStockStatus, orderStockStatusEnumBefore)
                .set(OrderEntity::getCustomerOrderStatus, update)
                .set(OrderEntity::getEmployeeOrderStatus, employeeUpdate)
                .set(OrderEntity::getOrderStockStatus, orderStockStatusEnumUpdate)
                .update();
    }

    @Override
    public List<OrderEntity> getByOrderStatus( OrderStatusEnum orderStatusEnum ) {
        if (orderStatusEnum == null) {
            return Lists.newArrayListWithCapacity(0);
        }
        return lambdaQuery()
                .eq(OrderEntity::getOrderStatus, orderStatusEnum)
                .list();
    }

    @Override
    public List<OrderEntity> getNeedUpdateTrackingNUmber() {
        return lambdaQuery()
                .isNotNull(OrderEntity::getWayBillNumber)
                .isNull(OrderEntity::getTrackingNumber)
                .list();
    }

    @Override
    public OrderEntity getByWayBillNumber( String wayBillNumber ) {
        return super.lambdaQuery()
                .eq(OrderEntity::getWayBillNumber, wayBillNumber)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .one();
    }

    @Override
    public void submitFulfillments( Collection<String> sysOrderIds, List<String> inStockOrderIds ) {
        if (CollUtil.isEmpty(sysOrderIds)) {
            return;
        }
        //- 提交履约时间
        //- 更新订单客户业务状态为：履约中
        //- 更新员工业务状态为：待审核
        //- ==是否更新订单状态为：shipO审核中 ==
        //- 更新订单标识为本地订单（所有订单数据不能被更新）
        Supplier<LambdaUpdateChainWrapper<OrderEntity>> defaultUpdateWrapper = () -> {
            LambdaUpdateChainWrapper<OrderEntity> lambdaUpdate = lambdaUpdate();
            lambdaUpdate
                    .set(OrderEntity::getFulfillmentTime, LocalDateTime.now())
                    .set(OrderEntity::getOrderStatus, OrderStatusEnum.SHIPO_AUDIT)
                    .set(OrderEntity::getCustomerBusinessStatus, CustomerBusinessStatusEnum.IN_PERFORMANCE)
                    .set(OrderEntity::getEmployeeBusinessStatus, EmployeeBusinessStatusEnum.PENDING_CHECK)
                    .set(OrderEntity::getLocalization, BoolEnum.YES);
            return lambdaUpdate;
        };


        Map<Boolean, List<String>> inStockOrderMap = QtStreamUtil.partition(sysOrderIds, inStockOrderIds::contains);
        List<String> inStockSysOrderIds = inStockOrderMap.get(true);

        if (CollUtil.isNotEmpty(inStockSysOrderIds)) {
            defaultUpdateWrapper.get()
                    .set(OrderEntity::getCustomerOrderStatus, CustomerOrderStatusEnum.IN_STOCK)
                    .set(OrderEntity::getEmployeeOrderStatus, EmployeeOrderStatusEnum.IN_STOCK)
                    .set(OrderEntity::getOrderStockStatus, OrderStockStatusEnum.IN_STOCK)
                    .in(OrderEntity::getSysOrderId, inStockSysOrderIds)
                    .update();
        }

        List<String> outOfStockSysOrderIds = inStockOrderMap.get(false);
        if (CollUtil.isNotEmpty(outOfStockSysOrderIds)) {
            defaultUpdateWrapper.get()
                    .set(OrderEntity::getCustomerOrderStatus, CustomerOrderStatusEnum.OUT_OF_STOCK)
                    .set(OrderEntity::getEmployeeOrderStatus, EmployeeOrderStatusEnum.OUT_OF_STOCK)
                    .set(OrderEntity::getOrderStockStatus, OrderStockStatusEnum.OUT_OF_STOCK)
                    .in(OrderEntity::getSysOrderId, outOfStockSysOrderIds)
                    .update();
        }
    }

    @Override
    public List<OrderEntity> listSubOrders( Collection<String> orderIds ) {
        return super.lambdaQuery()
                .select(OrderEntity::getId, OrderEntity::getSysOrderId, OrderEntity::getSourceOrderNumber)
                .in(OrderEntity::getSourceOrderNumber, orderIds)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .list();

    }

    @Override
    public OrderEntity getByOrderId( String sysOrderId, SFunction<OrderEntity, ?>... columns ) {
        if (Objects.isNull(columns)) {
            return this.getByOrderId(sysOrderId);
        }

        return super.lambdaQuery()
                .select(columns)
                .eq(OrderEntity::getSysOrderId, sysOrderId)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .one();
    }

    @Override
    public OrderEntity getByOrderId( String sysOrderId ) {
        return super.lambdaQuery()
                .eq(OrderEntity::getSysOrderId, sysOrderId)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .one();
    }

    @Override
    public OrderEntity deepMappingByOrderId( String sysOrderId ) {
        return Relation.mpjGetRelation(this.getByOrderId(sysOrderId), DeepConfig.defaultConfig());
    }

    @Override
    public boolean checkOrderExist( String sysShopId, String sysOrderId ) {
        return super.lambdaQuery()
                .eq(OrderEntity::getSysShopId, sysShopId)
                .eq(OrderEntity::getSysOrderId, sysOrderId)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .exists();
    }

    @Override
    public void deleteByOrderIds( List<String> sysOrderIds ) {
        if (CollUtil.isEmpty(sysOrderIds)) {
            return;
        }
        super.lambdaUpdate()
                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .remove();
    }

    @Override
    public void logicDeleteByOrderIds( Collection<String> sysOrderIds ) {
        if (CollUtil.isEmpty(sysOrderIds)) {
            return;
        }
        super.lambdaUpdate()
                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .set(OrderEntity::getDataStatus, DataStatusEnum.DELETE)
                .update();
    }

    @Override
    public List<OrderEntity> listShopOrders( Collection<String> sysShopIds,
                                             Collection<String> sysOrderIds,
                                             SFunction<OrderEntity, ?>... columns ) {

        if (CollUtil.isEmpty(sysOrderIds) || CollUtil.isEmpty(sysShopIds)) {
            return Lists.newArrayListWithCapacity(0);
        }

        LambdaQueryChainWrapper<OrderEntity> wrapper = super.lambdaQuery()
                .in(OrderEntity::getSysShopId, sysShopIds)
                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE);

        if (Objects.nonNull(columns)) {
            wrapper.select(columns);
        }

        return wrapper.list();
    }

    @Override
    public List<OrderEntity> listPendingProcessedSubOrders( Collection<String> sourceSysOrderIds ) {
        if (CollUtil.isEmpty(sourceSysOrderIds)) {
            return Lists.newArrayListWithCapacity(0);
        }
        return lambdaQuery()
                .select(OrderEntity::getSourceOrderNumber)
                .in(OrderEntity::getSourceOrderNumber, sourceSysOrderIds)
                .eq(OrderEntity::getCustomerBusinessStatus, CustomerBusinessStatusEnum.PENDING_PROCESSED)
                .list();
    }

    @Override
    public List<OrderEntity> listByJoinWrapper( MPJLambdaWrapper<OrderEntity> wrapper ) {
        return orderEntityMapper.selectList(wrapper);
    }

    @Override
    public boolean isLastFulfillmentSubOrder( OrderEntity orderEntity ) {
        String sysOrderId = orderEntity.getSysOrderId();
        Long platFormOrderId = orderEntity.getPlatFormOrderId();
        List<OrderEntity> orderEntityList = super.lambdaQuery()
                .ne(OrderEntity::getSysOrderId, sysOrderId)
                .eq(OrderEntity::getPlatFormOrderId, platFormOrderId)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .ne(OrderEntity::getShopifyFulfillmentFlag, BoolEnum.YES)
                .list();
        if (CollUtil.isEmpty(orderEntityList)) {
            return true;
        }
        return false;
    }

    private <T> void updateBizState( List<String> sysShopIds,
                                     List<String> sysOrderIds,
                                     SFunction<OrderEntity, T> column,
                                     T beforeState,
                                     OrderEntity updateData ) {


        if (CollUtil.isEmpty(sysOrderIds)) {
            return;
        }
        lambdaUpdate()
                .in(CollUtil.isNotEmpty(sysShopIds), OrderEntity::getSysShopId, sysShopIds)
                .in(OrderEntity::getSysOrderId, sysOrderIds)
                .eq(column, beforeState)
                .update(updateData);
    }

    @Override
    public List<String> queryOutStockOrderIdsByUserId(String userId) {
        // 获取用户所有的缺货订单
        List<String> shopIds = shopService.listShopIds(userId);
        if(CollUtil.isEmpty(shopIds)){
            return Lists.newArrayListWithCapacity(0);
        }
        // 获取用户所有缺货订单，按提交履约时间排序
        return QtStreamUtil.map(lambdaQuery()
                .select(OrderEntity::getSysOrderId)
                .in(OrderEntity::getSysShopId, shopIds)
                .eq(OrderEntity::getCustomerBusinessStatus, CustomerBusinessStatusEnum.IN_PERFORMANCE)
                .eq(OrderEntity::getCustomerOrderStatus, CustomerOrderStatusEnum.OUT_OF_STOCK)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .orderByAsc(OrderEntity::getFulfillmentTime)
                .list(), OrderEntity::getSysOrderId);

    }

    @Override
    public void batchUpdateTrackingNumById( List<OrderEntity> entityList ) {
        if (CollUtil.isNotEmpty(entityList)) {
            orderEntityMapper.batchUpdateTrackingNumById(entityList);
        }
    }

    @Override
    public OrderEntity getByOrderNumber( String sysShopId, String orderNumber ) {
        return super.lambdaQuery()
                .eq(OrderEntity::getOrderNumber, orderNumber)
                .eq(OrderEntity::getSysShopId, sysShopId)
                .eq(OrderEntity::getDataStatus, DataStatusEnum.ENABLE)
                .one();
    }
}
